package com.jike.shop.cloud.service;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.jike.shop.cloud.IPayService;
import com.jike.shop.cloud.common.exception.ApiException;
import com.jike.shop.cloud.dto.PaymentChannelDTO;
import com.jike.shop.cloud.dto.PaymentDTO;
import com.jike.shop.cloud.dto.PaymentResultDTO;
import com.jike.shop.cloud.dto.converter.PaymentChannelConverter;
import com.jike.shop.cloud.entity.Payment;
import com.jike.shop.cloud.entity.PaymentChannel;
import com.jike.shop.cloud.enums.PaymentChannelEnum;
import com.jike.shop.cloud.mapper.PaymentChannelMapper;
import com.jike.shop.cloud.mapper.PaymentMapper;
import com.jike.shop.cloud.mapper.PaymentNotificationLogMapper;
import com.jike.shop.cloud.mq.MQProducer;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@DubboService(group = "jike-shop", version = "1.0.0", protocol = "dubbo")
public class PayServiceImpl implements IPayService {

    @Autowired
    private PaymentMapper paymentMapper;

    @Autowired
    private PaymentChannelMapper paymentChannelMapper;

    @Autowired
    private PaymentNotificationLogMapper paymentNotificationLogMapper;

    @Autowired
    private PaymentChannelConverter paymentChannelConverter;

    @Autowired
    private Map<String, ChannelPayService> channelPayServiceMap;

    @Autowired
    private MQProducer mqProducer;

    //收银台服务的Dubbo测试接口
    @Override
    public String test(Long id) {
        return "success";
    }

    @Override
    public List<PaymentChannelDTO> listAvailablePaymentChannelList() {
        return paymentChannelMapper.selectList(Wrappers.lambdaQuery(PaymentChannel.class)
                        .orderByAsc(PaymentChannel::getSort)
                )
                .stream()
                .map(paymentChannelConverter::convert)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PaymentResultDTO pay(PaymentDTO paymentDTO) {
        if (paymentDTO == null) {
            return null;
        }

        PaymentChannel paymentChannel = paymentChannelMapper.selectOne(Wrappers.lambdaQuery(PaymentChannel.class)
                .eq(PaymentChannel::getCode, paymentDTO.getChannelCode())
        );
        if (paymentChannel == null) {
            return null;
        }

        Payment payment = paymentMapper.selectOne(Wrappers.lambdaQuery(Payment.class)
                .eq(Payment::getOrderNo, paymentDTO.getOrderNo())
        );
        if (payment == null) {
            payment = new Payment();
            payment.setAmount(paymentDTO.getAmount());
            payment.setChannelCode(paymentDTO.getChannelCode());
            payment.setClientIp(paymentDTO.getClientIp());
            payment.setOrderNo(paymentDTO.getOrderNo());
            payment.setStatus(Payment.STATE_INIT);
            paymentMapper.insert(payment);

            mqProducer.sendPaymentOutOfTimeCheckMessage(payment.getId());
        }

        if (payment.getStatus() == Payment.STATE_INIT) {
            int rows = paymentMapper.update(new Payment(), Wrappers.lambdaUpdate(Payment.class)
                    .set(Payment::getStatus, Payment.STATE_ACTING)
                    .eq(Payment::getStatus, Payment.STATE_INIT)
                    .eq(Payment::getId, payment.getId())
            );
            if (rows != 1) {
                throw new ApiException("正在支付中");
            }

            PaymentChannelEnum paymentChannelEnum = PaymentChannelEnum.getByCode(payment.getChannelCode());
            ChannelPayService channelPayService = channelPayServiceMap.get(paymentChannelEnum.getServiceName());
            if (channelPayService == null) {
                return null;
            }
            channelPayService.pay(payment);
        }

        PaymentResultDTO paymentResultDTO = new PaymentResultDTO();
        paymentResultDTO.setPaymentOrderNo(payment.getId());
        paymentResultDTO.setStatus(payment.getStatus());

        return paymentResultDTO;
    }
}
